//============================================================================
// Name        : evaluation-driven-training.cpp
// Author      : Kibeom Kim
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#ifndef NUMBER_H
#define NUMBER_H

#include <iostream>
#include <cmath>
#include <numeric>

template<class T, size_t N>
class Number{
#define FORALL(i, expressions) for(size_t i = 0; i < N; ++i){expressions}
//	template<class Func> void FORALL(Func f){
//		for (size_t i = 0; i < N; ++i){
//			f(i);
//		}
//	}
//	FORALL([&](size_t i){d_vars[i] = 0;});
private:
	T d_var[N];

public:
	T var;

	inline Number(){
	}

	inline Number(T value){
		var = value;
		FORALL(i, d_var[i] = T(0);)
	}

	inline Number(size_t index, T value){ //TODO chain constructors, when the compiler supports gcc4.7.x
		var = value;
		FORALL(i, d_var[i] = T(0);)
		d_var[index] = T(1);
	}

	explicit inline operator T() const{
		return var;
	}

	inline T operator[](size_t n) const{
		return d_var[n];
	}

	inline T& operator[](size_t n){
		return d_var[n];
	}

/////////////// Unary operators
	inline Number operator+() const{
		return Number(*this);
	}

	inline Number operator-() const{
		Number result;
		FORALL(i, result[i] = -d_var[i];)
		result.var = -result.var;
		return result;
	}

///////////////// Binary operators
	inline Number& operator+=(const Number& rhs){
		var += rhs.var;
		FORALL(i, d_var[i] += rhs[i];)
		return *this;
	}

	inline Number& operator-=(const Number& rhs){
		FORALL(i, d_var[i] -= rhs[i];)
		var -= rhs.var;
		return *this;
	}

	inline Number& operator*=(const Number& rhs){
		FORALL(i, d_var[i] = (var * rhs[i]) + (d_var[i] * rhs.var);)
		var *= rhs.var;
		return *this;
	}

	inline Number& operator/=(const Number& rhs){
		FORALL(
				i,
				d_var[i] = (rhs.var * d_var[i] - var * rhs[i]) / (rhs.var * rhs.var);)
//Todo what if 0/0?
//Todo efficient computing by caching (1/(rhs.vars*rhs.vars))?
		var /= rhs.var;
		return *this;
	}
	//////////////////////
	inline Number& operator++(){
		return *this += 1;
	}
	inline Number& operator--(){
		return *this -= 1;
	}
	inline Number operator++(int){
		Number result = *this;
		*this += 1;
		return result;
	}
	inline Number operator--(int){
		Number result = *this;
		*this -= 1;
		return result;
	}
//////////////////

	inline Number& operator+=(const T& rhs){
		var += rhs;
		return *this;
	}

	inline Number& operator-=(const T& rhs){
		var -= rhs;
		return *this;
	}

	inline Number& operator*=(const T& rhs){
		FORALL(i, d_var[i] *= rhs;)
		var *= rhs;
		return *this;
	}

	inline Number& operator/=(const T& rhs){
		FORALL(i, d_var[i] /= rhs;)
		var /= rhs;
		return *this;
	}

////////////
	template<class S>
	inline Number operator+(const S& rhs) const{
		Number result = *this;
		result += rhs;
		return result;
	}

	template<class S>
	inline Number operator-(const S& rhs) const{
		Number result = *this;
		result -= rhs;
		return result;
	}

	template<class S>
	inline Number operator*(const S& rhs) const{
		Number result = *this;
		result *= rhs;
		return result;
	}

	template<class S>
	inline Number operator/(const S& rhs) const{
		Number result = *this;
		result /= rhs;
		return result;
	}

///////////
	friend inline Number operator+(const T& lhs, const Number& rhs){
		return rhs + lhs;
	}

	friend inline Number operator-(const T& lhs, const Number& rhs){
		return (-rhs) + lhs;
	}

	friend inline Number operator*(const T& lhs, const Number& rhs){
		return rhs * lhs;
	}

	friend inline Number operator/(const T& lhs, const Number& rhs){
		Number result;
		result.var = lhs / rhs.var; //Todo check if it's right
		FORALL(i, result[i] = (-lhs)*rhs[i] / (rhs.var*rhs.var);)
		return result;
	}

	//Comparisons
	template<class S>
	inline bool operator==(const S& rhs) const{
		return var == T(rhs);
		//TODO one might expect true for sufficiently close two floating points
		//TODO what if one wants to compare including d_var??
	}

	template<class S>
	inline bool operator!=(const S& rhs) const{
		return var != T(rhs);
	}

	template<class S>
	inline bool operator>(const S& rhs) const{
		return var > T(rhs);
	}

	template<class S>
	inline bool operator<(const S& rhs) const{
		return var < T(rhs);
	}

	template<class S>
	inline bool operator>=(const S& rhs) const{
		return var >= T(rhs);
	}

	template<class S>
	inline bool operator<=(const S& rhs) const{
		return var <= T(rhs);
	}

	friend inline bool operator==(const T& lhs, const Number& rhs){
		return lhs == rhs;
	}

	friend inline bool operator!=(const T& lhs, const Number& rhs){
		return lhs != T(rhs);
	}

	friend inline bool operator>(const T& lhs, const Number& rhs){
		return lhs > T(rhs);
	}

	friend inline bool operator<(const T& lhs, const Number& rhs){
		return lhs < T(rhs);
	}

	friend inline bool operator>=(const T& lhs, const Number& rhs){
		return lhs >= T(rhs);
	}

	friend inline bool operator<=(const T& lhs, const Number& rhs){
		return lhs <= T(rhs);
	}

////////////////////
	friend inline Number exp(const Number& hs){
		Number result;
		FORALL(i, result[i] = hs[i] * exp(hs.var);)
// TODO efficient by storing sin(hs.var);
		result.var = exp(hs.var);
		return result;
	}
	friend inline Number log(const Number& hs){
		Number result;
		FORALL(i, result[i] = hs[i] == 0 ? T(0) : hs[i] / hs.var;)
//Todo because of when hs[i]==0 and hs.var=0. But is this the best in terms of performance?
		result.var = log(hs.var);
		return result;
	}
	friend inline Number pow(const Number& lhs, const T& rhs){ //Todo when rhs=Number, when rhs==?
		Number result;
		FORALL(i, result[i] = lhs[i] * rhs * pow(lhs.var, rhs - 1);)
		result.var = pow(lhs.var, rhs);
		return result;
	}
	friend inline Number sqrt(const Number& hs){
		Number result;
		FORALL(i, result[i] = hs[i] / (2*sqrt(hs.var));)
		result.var = sqrt(hs.var);
		return result;
	}
	///////////////////////
	friend inline Number cos(const Number& hs){
		Number result;
		FORALL(i, result[i] = -hs[i] * sin(hs.var);)
		result.var = cos(hs.var);
		return result;
	}

	friend inline Number sin(const Number& hs){
		Number result;
		FORALL(i, result[i] = hs[i] * cos(hs.var);)
		result.var = sin(hs.var);
		return result;
	}

	friend inline Number tan(const Number& hs){
		Number result;
		FORALL(i, result[i] = 2*hs[i] / (cos(2*hs.var)+1);)
//Todo what if 0/0?
		result.var = tan(hs.var);
		return result;
	}

//	friend inline Number acos(Number& hs){
//		Number result;
//		result.var = acos(hs.var);
//		FORALL(i, result[i] = -hs[i] * sin(hs.var);)
//		return result;
//	}
//
//	friend inline Number asin(Number& hs){
//		Number result;
//		result.var = asin(hs.var);
//		FORALL(i, result[i] = -hs[i] * sin(hs.var);)
//		return result;
//	}
//
//	friend inline Number atan(Number& hs){
//		Number result;
//		result.var = atan(hs.var);
//		FORALL(i, result[i] = -hs[i] * sin(hs.var);)
//		return result;
//	}
//
//	friend inline Number atan2(Number& hs){
//		Number result;
//		result.var = atan2(hs.var);
//		FORALL(i, result[i] = -hs[i] * sin(hs.var);)
//		return result;
//}#undef FORALL
};
//template<unsigned N>
//using std::numeric_limits<Number<double,N>> = std::numeric_limits<double>;

namespace std
{
	template <typename T, size_t N> //Todo we can probably use template alias feature in c++
	struct numeric_limits<Number<T, N>> : numeric_limits<T>{};
//	template <typename T, size_t N>
//	using numeric_limits<Number<T, N>> = numeric_limits<T>;
}

#endif
